﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Nana.String
{
  public static class StringExt
  {
    private static unsafe int pFindTokenChar(this string s, int start)
    {
      int num = s.Length - start;

      fixed (char* cpStr = s)
      {
        char* cpCurr;
        cpCurr = cpStr + start;

        while (num > 0)
        {
          char chr = *cpCurr;
          if (chr <= '\\')
          {
            switch (chr)
            {
              case '\0':
              case '\n':
              case '\r':
              case '"':
              case '/':
              case '\'':
              case '\\':
                return s.Length - num;
            }
          }
          else if (chr == '\u2028' || chr == '\u2029')
          {
            return s.Length - num;
          }

          cpCurr++; // next char
          num--;
        } // no more char
      }

      return -1;
    }
    public static string JsEncode(this string s)
    {
      if (s == null)
        return null;

      int idx = s.pFindTokenChar(0);
      if (idx == -1)
        return s;

      int len = s.Length;
      int tidx;
      //int llen = len - 1;
      StringBuilder sb = new StringBuilder(len + 0x40);

      int i = 0;
      do
      {
        if (idx > i)
          sb.Append(s, i, idx - i);

        char c = s[idx];
        //char prev = '\0';
        switch (c)
        {
          case '\0':
            sb.Append(@"\000");
            break;
          case '\n':
            sb.Append(@"\n\
");
            break;
          case '\r':
            if ((tidx = idx + 1) < len && s[tidx] == '\n')
              sb.Append(@"\r");
            else
              sb.Append(@"\r\
");
            break;
          case '"':
            sb.Append(@"\""");
            break;
          case '/':
            if (idx > 0 && s[idx - 1] == '<')
              sb.Append(@"\/");
            else
              sb.Append('/');
            break;
          case '\'':
            sb.Append(@"\'");
            break;
          case '\\':
            sb.Append(@"\\");
            break;
          default:
            sb.Append(@"\u");
            sb.Append(((int)c).ToString("x4"));
            break;
        }

        i = idx + 1;
        if (i < len)
          idx = s.pFindTokenChar(i);
        else
          break;

      } while (idx != -1);

      sb.Append(s, i, len - i);
      return sb.ToString();
    }
  }
}
